Skip to content

Conversation

ahatanak
Copy link
Collaborator

@ahatanak ahatanak commented Sep 15, 2025

This change fixes the crash in clang's CodeGen by erroring out in Sema if those arguments are passed.

rdar://139824423

class, or complex types are passed to _builtin_os_log_format

This change fixes a crash in clang's CodeGen by ensuring that those
arguments are ignored.

rdar://139824423
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Sep 15, 2025
@llvmbot
Copy link
Member

llvmbot commented Sep 15, 2025

@llvm/pr-subscribers-clang

Author: Akira Hatanaka (ahatanak)

Changes

This change fixes a crash in clang's CodeGen by ensuring that those arguments are ignored.

rdar://139824423


Full diff: https://github.com/llvm/llvm-project/pull/158744.diff

2 Files Affected:

  • (modified) clang/lib/AST/OSLog.cpp (+32-16)
  • (modified) clang/test/CodeGenObjC/os_log.m (+23)
diff --git a/clang/lib/AST/OSLog.cpp b/clang/lib/AST/OSLog.cpp
index 91f8410e89e86..d228d297dd2ca 100644
--- a/clang/lib/AST/OSLog.cpp
+++ b/clang/lib/AST/OSLog.cpp
@@ -70,22 +70,31 @@ class OSLogFormatStringHandler
   bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
                              const char *StartSpecifier, unsigned SpecifierLen,
                              const TargetInfo &) override {
+    // Set the argument expression. Arguments of struct/class/complex types are
+    // ignored.
+    auto CheckAndSetArgExpr = [&](unsigned Idx, auto &ArgE) {
+      const Expr *E = Args[Idx];
+      if (E && (E->getType()->isRecordType() || E->getType()->isComplexType()))
+        return false;
+      ArgE = E;
+      return true;
+    };
+
     if (!FS.consumesDataArgument() &&
         FS.getConversionSpecifier().getKind() !=
             clang::analyze_format_string::ConversionSpecifier::PrintErrno)
       return true;
 
-    ArgsData.emplace_back();
+    ArgData ArgD;
     unsigned ArgIndex = FS.getArgIndex();
     if (ArgIndex < Args.size())
-      ArgsData.back().E = Args[ArgIndex];
+      if (!CheckAndSetArgExpr(ArgIndex, ArgD.E))
+        return true;
 
     // First get the Kind
-    ArgsData.back().Kind = getKind(FS.getConversionSpecifier().getKind());
-    if (ArgsData.back().Kind != OSLogBufferItem::ErrnoKind &&
-        !ArgsData.back().E) {
+    ArgD.Kind = getKind(FS.getConversionSpecifier().getKind());
+    if (ArgD.Kind != OSLogBufferItem::ErrnoKind && !ArgD.E) {
       // missing argument
-      ArgsData.pop_back();
       return false;
     }
 
@@ -97,10 +106,11 @@ class OSLogFormatStringHandler
       case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%s"
         break;
       case clang::analyze_format_string::OptionalAmount::Constant: // "%.16s"
-        ArgsData.back().Size = precision.getConstantAmount();
+        ArgD.Size = precision.getConstantAmount();
         break;
       case clang::analyze_format_string::OptionalAmount::Arg: // "%.*s"
-        ArgsData.back().Count = Args[precision.getArgIndex()];
+        if (!CheckAndSetArgExpr(precision.getArgIndex(), ArgD.Count))
+          return true;
         break;
       case clang::analyze_format_string::OptionalAmount::Invalid:
         return false;
@@ -113,10 +123,11 @@ class OSLogFormatStringHandler
       case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%P"
         return false; // length must be supplied with pointer format specifier
       case clang::analyze_format_string::OptionalAmount::Constant: // "%.16P"
-        ArgsData.back().Size = precision.getConstantAmount();
+        ArgD.Size = precision.getConstantAmount();
         break;
       case clang::analyze_format_string::OptionalAmount::Arg: // "%.*P"
-        ArgsData.back().Count = Args[precision.getArgIndex()];
+        if (!CheckAndSetArgExpr(precision.getArgIndex(), ArgD.Count))
+          return true;
         break;
       case clang::analyze_format_string::OptionalAmount::Invalid:
         return false;
@@ -125,22 +136,27 @@ class OSLogFormatStringHandler
     }
     default:
       if (FS.getPrecision().hasDataArgument()) {
-        ArgsData.back().Precision = Args[FS.getPrecision().getArgIndex()];
+        if (!CheckAndSetArgExpr(FS.getPrecision().getArgIndex(),
+                                ArgD.Precision))
+          return true;
       }
       break;
     }
     if (FS.getFieldWidth().hasDataArgument()) {
-      ArgsData.back().FieldWidth = Args[FS.getFieldWidth().getArgIndex()];
+      if (!CheckAndSetArgExpr(FS.getFieldWidth().getArgIndex(),
+                              ArgD.FieldWidth))
+        return true;
     }
 
     if (FS.isSensitive())
-      ArgsData.back().Flags |= OSLogBufferItem::IsSensitive;
+      ArgD.Flags |= OSLogBufferItem::IsSensitive;
     else if (FS.isPrivate())
-      ArgsData.back().Flags |= OSLogBufferItem::IsPrivate;
+      ArgD.Flags |= OSLogBufferItem::IsPrivate;
     else if (FS.isPublic())
-      ArgsData.back().Flags |= OSLogBufferItem::IsPublic;
+      ArgD.Flags |= OSLogBufferItem::IsPublic;
 
-    ArgsData.back().MaskType = FS.getMaskType();
+    ArgD.MaskType = FS.getMaskType();
+    ArgsData.push_back(ArgD);
     return true;
   }
 
diff --git a/clang/test/CodeGenObjC/os_log.m b/clang/test/CodeGenObjC/os_log.m
index 837883ec4bb75..00229a746304c 100644
--- a/clang/test/CodeGenObjC/os_log.m
+++ b/clang/test/CodeGenObjC/os_log.m
@@ -13,6 +13,13 @@ + (id)m1;
 
 C *c;
 
+struct S {
+  int a[4];
+};
+
+struct S s;
+_Complex float cf;
+
 @class NSString;
 extern __attribute__((visibility("default"))) NSString *GenString(void);
 void os_log_pack_send(void *);
@@ -123,3 +130,19 @@ void test_builtin_os_log5(void *buf) {
   __builtin_os_log_format(buf, "capabilities: %@", (0, GenString()));
   os_log_pack_send(buf);
 }
+
+// CHECK-LABEL: define void @test_builtin_os_log6(
+// CHECK: call void @__os_log_helper_1_0_0(
+
+void test_builtin_os_log6(void *buf) {
+  __builtin_os_log_format(buf, "%.*s %.*P %*.*f", s, s, s, s, s, s, s);
+}
+
+// CHECK-LABEL: define linkonce_odr hidden void @__os_log_helper_1_0_0(
+
+// CHECK-LABEL: define void @test_builtin_os_log7(
+// CHECK: call void @__os_log_helper_1_0_0(
+
+void test_builtin_os_log7(void *buf) {
+  __builtin_os_log_format(buf, "%.*s %.*P %*.*f", cf, cf, cf, cf, cf, cf, cf);
+}

@apple-fcloutier apple-fcloutier self-requested a review September 16, 2025 00:27
@apple-fcloutier
Copy link
Contributor

I repro'd the original problem at desk:

% cat test.cpp
#include <vector>

void foo(void *buf) {
	std::vector<char> vec;
	__builtin_os_log_format(buf, "%s", vec);
}
% clang++ -Wformat -Wformat=2 -o test test.cpp
test.cpp:5:37: error: cannot pass non-trivial object of type 'std::vector<char>' to variadic function; expected type from format string was 'char *' [-Wnon-pod-varargs]
    5 |         __builtin_os_log_format(buf, "%s", vec);
      |                                       ~~   ^~~
test.cpp:5:37: error: cannot compile this scalar expression yet
    5 |         __builtin_os_log_format(buf, "%s", vec);
      |                                            ^~~
clang++: error: unable to execute command: Segmentation fault: 11
...

"cannot compile this %0 yet" is a codegen diagnostic and the crash doesn't reproduce if I also pass -fsyntax-only. I understand that we have to do something regardless because you can turn off -Wnon-pod-varargs, but how come we get to codegen after an error was emitted?

const char *StartSpecifier, unsigned SpecifierLen,
const TargetInfo &) override {
// Set the argument expression. Arguments of struct/class/complex types are
// ignored.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason we can't diagnose this as an error? A priori, it seems no one can be doing this right now because it crashes clang. The layout is going to be super broken if we skip arguments.

@ahatanak
Copy link
Collaborator Author

"cannot compile this %0 yet" is a codegen diagnostic and the crash doesn't reproduce if I also pass -fsyntax-only. I understand that we have to do something regardless because you can turn off -Wnon-pod-varargs, but how come we get to codegen after an error was emitted?

We get to CodeGen because warnings aren't unrecoverable errors.

@ahatanak ahatanak merged commit 8a0d145 into llvm:main Sep 23, 2025
9 checks passed
@ahatanak ahatanak deleted the fix-os-log-crash branch September 23, 2025 19:21
@llvm-ci
Copy link
Collaborator

llvm-ci commented Sep 23, 2025

LLVM Buildbot has detected a new failure on builder clang-m68k-linux-cross running on suse-gary-m68k-cross while building clang at step 4 "build stage 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/27/builds/16491

Here is the relevant piece of the build log for the reference
Step 4 (build stage 1) failure: 'ninja' (failure)
...
[165/368] Building CXX object tools/clang/lib/Parse/CMakeFiles/obj.clangParse.dir/ParseOpenMP.cpp.o
[166/368] Building CXX object tools/clang/lib/Parse/CMakeFiles/obj.clangParse.dir/ParseTentative.cpp.o
[167/368] Linking CXX static library lib/libLLVMLTO.a
[168/368] Linking CXX static library lib/libLLVMAsmPrinter.a
[169/368] Building CXX object tools/clang/lib/Serialization/CMakeFiles/obj.clangSerialization.dir/ASTReaderStmt.cpp.o
[170/368] Building CXX object tools/clang/lib/Frontend/CMakeFiles/obj.clangFrontend.dir/ASTUnit.cpp.o
[171/368] Building CXX object tools/clang/lib/Serialization/CMakeFiles/obj.clangSerialization.dir/ASTWriterDecl.cpp.o
[172/368] Building CXX object tools/clang/lib/Frontend/CMakeFiles/obj.clangFrontend.dir/FrontendActions.cpp.o
[173/368] Building CXX object tools/clang/lib/Sema/CMakeFiles/obj.clangSema.dir/SemaTemplateDeductionGuide.cpp.o
[174/368] Building CXX object tools/clang/lib/Sema/CMakeFiles/obj.clangSema.dir/SemaExpr.cpp.o
FAILED: tools/clang/lib/Sema/CMakeFiles/obj.clangSema.dir/SemaExpr.cpp.o 
/usr/bin/c++ -DCLANG_EXPORTS -DGTEST_HAS_RTTI=0 -D_DEBUG -D_GLIBCXX_ASSERTIONS -D_GLIBCXX_USE_CXX11_ABI=1 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/tools/clang/lib/Sema -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/lib/Sema -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/include -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/tools/clang/include -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/include -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/llvm/include -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wimplicit-fallthrough -Wno-uninitialized -Wno-nonnull -Wno-class-memaccess -Wno-dangling-reference -Wno-redundant-move -Wno-pessimizing-move -Wno-array-bounds -Wno-stringop-overread -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wno-misleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -fno-common -Woverloaded-virtual -O3 -DNDEBUG -std=c++17  -fno-exceptions -funwind-tables -fno-rtti -UNDEBUG -MD -MT tools/clang/lib/Sema/CMakeFiles/obj.clangSema.dir/SemaExpr.cpp.o -MF tools/clang/lib/Sema/CMakeFiles/obj.clangSema.dir/SemaExpr.cpp.o.d -o tools/clang/lib/Sema/CMakeFiles/obj.clangSema.dir/SemaExpr.cpp.o -c /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/lib/Sema/SemaExpr.cpp
c++: fatal error: Killed signal terminated program cc1plus
compilation terminated.
[175/368] Building CXX object tools/clang/lib/StaticAnalyzer/Frontend/CMakeFiles/obj.clangStaticAnalyzerFrontend.dir/ModelInjector.cpp.o
[176/368] Building CXX object tools/clang/lib/Frontend/CMakeFiles/obj.clangFrontend.dir/ModuleDependencyCollector.cpp.o
[177/368] Building CXX object tools/clang/lib/Index/CMakeFiles/obj.clangIndex.dir/IndexingAction.cpp.o
[178/368] Building CXX object tools/clang/lib/Frontend/CMakeFiles/obj.clangFrontend.dir/TestModuleFileExtension.cpp.o
[179/368] Building CXX object tools/clang/lib/Tooling/DependencyScanning/CMakeFiles/obj.clangDependencyScanning.dir/DependencyScanningTool.cpp.o
[180/368] Building CXX object tools/clang/lib/Frontend/CMakeFiles/obj.clangFrontend.dir/FrontendAction.cpp.o
[181/368] Building CXX object tools/clang/lib/Frontend/CMakeFiles/obj.clangFrontend.dir/InitPreprocessor.cpp.o
[182/368] Building CXX object tools/clang/lib/Frontend/CMakeFiles/obj.clangFrontend.dir/CompilerInstance.cpp.o
[183/368] Building CXX object tools/clang/lib/Frontend/CMakeFiles/obj.clangFrontend.dir/PrecompiledPreamble.cpp.o
[184/368] Building CXX object tools/clang/lib/Sema/CMakeFiles/obj.clangSema.dir/SemaTemplateInstantiate.cpp.o
[185/368] Building CXX object tools/clang/tools/extra/clang-tidy/CMakeFiles/obj.clangTidy.dir/ExpandModularHeadersPPCallbacks.cpp.o
[186/368] Building CXX object tools/clang/lib/Tooling/DependencyScanning/CMakeFiles/obj.clangDependencyScanning.dir/DependencyScanningWorker.cpp.o
[187/368] Building CXX object tools/clang/lib/Tooling/DependencyScanning/CMakeFiles/obj.clangDependencyScanning.dir/ModuleDepCollector.cpp.o
[188/368] Building CXX object tools/clang/lib/CodeGen/CMakeFiles/obj.clangCodeGen.dir/CGException.cpp.o
[189/368] Building CXX object tools/clang/lib/Serialization/CMakeFiles/obj.clangSerialization.dir/ASTReaderDecl.cpp.o
[190/368] Building CXX object tools/clang/lib/Interpreter/CMakeFiles/obj.clangInterpreter.dir/CodeCompletion.cpp.o
[191/368] Building CXX object tools/clang/lib/Sema/CMakeFiles/obj.clangSema.dir/SemaTemplateDeduction.cpp.o
In file included from /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/lib/Sema/TreeTransform.h:48,
                 from /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/lib/Sema/SemaTemplateDeduction.cpp:13:
In constructor ‘clang::LocalInstantiationScope::LocalInstantiationScope(clang::Sema&, bool, bool)’,
    inlined from ‘clang::TemplateDeductionResult clang::Sema::DeduceTemplateArguments(clang::FunctionTemplateDecl*, clang::TemplateArgumentListInfo*, llvm::ArrayRef<clang::Expr*>, clang::FunctionDecl*&, clang::sema::TemplateDeductionInfo&, bool, bool, bool, clang::QualType, clang::Expr::Classification, bool, llvm::function_ref<bool(llvm::ArrayRef<clang::QualType>, bool)>)’ at /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/lib/Sema/SemaTemplateDeduction.cpp:4574:42:
/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/include/clang/Sema/Template.h:438:41: warning: storing the address of local variable ‘InstScope’ in ‘*this.clang::Sema::CurrentInstantiationScope’ [-Wdangling-pointer=]
  438 |       SemaRef.CurrentInstantiationScope = this;
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/lib/Sema/SemaTemplateDeduction.cpp: In member function ‘clang::TemplateDeductionResult clang::Sema::DeduceTemplateArguments(clang::FunctionTemplateDecl*, clang::TemplateArgumentListInfo*, llvm::ArrayRef<clang::Expr*>, clang::FunctionDecl*&, clang::sema::TemplateDeductionInfo&, bool, bool, bool, clang::QualType, clang::Expr::Classification, bool, llvm::function_ref<bool(llvm::ArrayRef<clang::QualType>, bool)>)’:
/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/lib/Sema/SemaTemplateDeduction.cpp:4574:27: note: ‘InstScope’ declared here
 4574 |   LocalInstantiationScope InstScope(*this);
      |                           ^~~~~~~~~
/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/lib/Sema/SemaTemplateDeduction.cpp:4533:73: note: ‘this’ declared here
 4533 |     llvm::function_ref<bool(ArrayRef<QualType>, bool)> CheckNonDependent) {
      |                                                                         ^
[192/368] Building CXX object tools/clang/lib/Frontend/Rewrite/CMakeFiles/obj.clangRewriteFrontend.dir/FrontendActions.cpp.o
[193/368] Building CXX object tools/clang/lib/CodeGen/CMakeFiles/obj.clangCodeGen.dir/CGStmt.cpp.o
[194/368] Building CXX object tools/clang/lib/AST/CMakeFiles/obj.clangAST.dir/ByteCode/Interp.cpp.o
[195/368] Building CXX object tools/clang/tools/libclang/CMakeFiles/libclang.dir/CIndexCodeCompletion.cpp.o

ahatanaka pushed a commit to swiftlang/llvm-project that referenced this pull request Sep 23, 2025
…ass, or complex types are passed to _builtin_os_log_format (llvm#158744)

This change fixes the crash in clang's CodeGen by erroring out in Sema
if those arguments are passed.

rdar://139824423
ahatanaka pushed a commit to swiftlang/llvm-project that referenced this pull request Sep 23, 2025
…ass, or complex types are passed to _builtin_os_log_format (llvm#158744)

This change fixes the crash in clang's CodeGen by erroring out in Sema
if those arguments are passed.

rdar://139824423
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants